/*
 * Copyright 2009, Team Mondegreen:
 * Brian Hsieh - bh2181@columbia.edu
 * Eugene Yao - eyy2102@columbia.edu
 * Jake Amonette - jaa2149@columbia.edu
 * Tal Giat - tg2235@colubia.edu
 * 
 * This file is part of AMFM (A Minimal Fractal Music Language), a language created for
 * fractal music as part of the Programming Languages and Translator class at Columbia university.
 *
 * For more details go to: http://code.google.com/p/amfm/
 * 
 * AMFM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * at your option any later version.
 * 
 * AMFM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with AMFM.  If not, see <http://www.gnu.org/licenses/>. 
 */

package org.mondegreen.amfm.compiler;

/**
 * A 2D matrix of bits stores as an array of integers, and provided set and get
 * operations. This way we can store martix of boolean using much less memory
 * (bit for each entry vs. byte)
 * 
 * @author talgiat
 * 
 */
public class BitMatrix2D {
	private final int rows;
	private final int columns;
	private int[] data;
	
	/**
	 * Create matrix with given size
	 * @param rows how many rows
	 * @param columns how many columns
	 */
	public BitMatrix2D(int rows, int columns) {
		this.rows = rows;
		this.columns = columns;

		/* x >> 5 = x / 32, only faster */
		this.data = new int[Math.max(1, (rows * columns + 1) >> 5)];
	}

	public int rows() {
		return rows;
	}

	public int columns() {
		return columns;
	}

	/**
	 * Is the entry in given row an column on or off
	 * @param row the row index
	 * @param col the column index
	 * @return true if the entry is on (1), false otherwise
	 * @throws IndexOutOfBoundsException if the given entry is not within the matrix boundaries
	 */
	public boolean get(int row, int col) throws IndexOutOfBoundsException {
		if (row < 0 || row >= rows)
			throw new IndexOutOfBoundsException("Row index out of bounds:" + row);
		else if (col < 0 || col >= columns)
			throw new IndexOutOfBoundsException("Column index out of bounds:" + col);

		int i = row * columns + col;
		return ((data[i >> 5] >> (i % 32)) & 1) != 0;
	}

	/**
	 * Set an entry in given row an column to be on or off
	 * @param row the row index
	 * @param col the column index
	 * @param v the value to set, true will turn the bit on in the given entry false off
	 * @throws IndexOutOfBoundsException if the given entry is not within the matrix boundaries
	 */
	public void set(int row, int col, boolean v) throws IndexOutOfBoundsException {
		if (row < 0 || row >= rows)
			throw new IndexOutOfBoundsException("Row index out of bounds:"
					+ row);
		else if (col < 0 || col >= columns)
			throw new IndexOutOfBoundsException("Column index out of bounds:"
					+ col);

		int i = row * columns + col;
		int idiv32 = i >> 5;
		int modBit = 1 << (i % 32);
		data[idiv32] = v ? data[idiv32] | modBit : data[idiv32] & ~modBit;
	}
}
